// Environment code for project cleaningBot

import jason.asSyntax.Literal;
import jason.asSyntax.NumberTerm;
import jason.asSyntax.Structure;
import jason.asSyntax.Term;
import jason.environment.Environment;
import jason.environment.grid.GridWorldModel;
import jason.environment.grid.GridWorldView;
import jason.environment.grid.Location;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;
import java.util.logging.Logger;

public class room extends Environment {
	public static final int GSize = 20; // grid size
	public static final int GARB =  16; // garbage code in grid model

	public static final int OBSTACLE = 17 ;

	public static final Term bg = Literal.parseLiteral("burn(garb)");
	public static final Term gotoClosest = Literal.parseLiteral("gotoClosest");
	public static final Literal g1 = Literal.parseLiteral("garbage(r1)");

	private Logger logger = Logger.getLogger("cleaningBot."
			+ room.class.getName());

	private RoomModel model; //
	private RoomView view;

	public int GARBNumber() { // ham dem so rac hien co tren moi truong
		return model.countObjects(GARB);
	}

	// ham add garb vao mang
	public Point[] garbArray() {
		Point[] array = new Point[model.countObjects(GARB)];
		int garbCount = 0;
		for (int i = 0; i < model.getHeight(); i++)
			for (int j = 0; j < model.getWidth(); j++) {
				if (model.hasObject(GARB, i, j)) {
					garbCount++;
					array[garbCount - 1] = new Point(i, j);
				}
			}
		return array;
	}

	
	public Point closestPoint() {
		Location r1Loc = model.getAgPos(0);
	
		Point closestGarb = new Point(garbArray()[0].x, garbArray()[0].y);
		for (int i = 0; i < garbArray().length; i++) {
			if (model.getDistance(r1Loc.x, r1Loc.y, garbArray()[i].x,
					garbArray()[i].y) < model.getDistance(r1Loc.x, r1Loc.y,
					closestGarb.x, closestGarb.y)) {
				closestGarb = garbArray()[i];
			}
		}
		return closestGarb;
	}

	/** Called before the MAS execution with the args informed in .mas2j */
	@Override
	public void init(String[] args) {
		model = new RoomModel();
		view = new RoomView(model);
		super.init(args);
		addPercept(Literal.parseLiteral("percept(demo)"));
	}

	@Override
	public boolean executeAction(String agName, Structure action) {
		updatePercepts();
		logger.info(agName + " doing: " + action);
		try {
			if (action.getFunctor().equals("move_towards")) {
				int x = (int) ((NumberTerm) action.getTerm(0)).solve();// getTerm(0) => ??? 
				int y = (int) ((NumberTerm) action.getTerm(1)).solve();
				model.moveTowards(x, y);
				updatePercepts();
			} else if (action.equals(bg)) {   //   
				model.burnGarb();
				updatePercepts();
			} else if (action.equals(gotoClosest)) {
				model.moveTowards(this.closestPoint().x, this.closestPoint().y);
				updatePercepts();
			} else {
				updatePercepts();
				return false;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		updatePercepts();

		try {
			Thread.sleep(2000); // toc do xu ly hien thi nhanh or cham
		} catch (Exception e) {
		}
		informAgsEnvironmentChanged();  //  ????? 
		return true;
	}

	void updatePercepts() {
		clearPercepts();

		Location r1Loc = model.getAgPos(0);
		Literal pos1 = Literal.parseLiteral("pos(r1," + (r1Loc.x) + ","
				+ r1Loc.y + ")");
		addPercept(pos1);
		if (model.hasObject(GARB, r1Loc)) {
			addPercept(g1);
		}

	}

	/** Called before the end of MAS execution */
	@Override
	public void stop() {
		super.stop();
	}

	class RoomModel extends GridWorldModel {

		public static final int MErr = 2; // max error in pick garb
		int nerr; // number of tries of pick garb
		boolean r1HasGarb = false; // whether r1 is carrying garbage or not

		Random random = new Random(System.currentTimeMillis());

		private RoomModel() {
			super(GSize, GSize, 2);

			// initial location of agents
			try {
				
				setAgPos(0, 0, 0);
				
			} catch (Exception e) {
				e.printStackTrace();
			}

			// initial location of obstacle
			add(OBSTACLE, 2, 3);
			add(OBSTACLE, 2, 4);
			add(OBSTACLE, 2, 5);
			add(OBSTACLE, 2, 6);
			add(OBSTACLE, 2, 7);

			add(OBSTACLE, 2, 12);
			add(OBSTACLE, 2, 13);
			add(OBSTACLE, 2, 14);
			add(OBSTACLE, 2, 15);
			add(OBSTACLE, 2, 16);
		

			add(OBSTACLE, 14, 2);
			add(OBSTACLE, 15, 2);
			add(OBSTACLE, 16, 2);
			add(OBSTACLE, 17, 2);
			add(OBSTACLE, 17, 3);
			add(OBSTACLE, 17, 4);
			add(OBSTACLE, 17, 5);
			add(OBSTACLE, 17, 6);
			add(OBSTACLE, 17, 7);

			add(OBSTACLE, 17, 12);
			add(OBSTACLE, 17, 13);
			add(OBSTACLE, 17, 14);
			add(OBSTACLE, 17, 15);
			add(OBSTACLE, 17, 16);
			add(OBSTACLE, 16, 17);

			add(OBSTACLE, 8, 4);
			add(OBSTACLE, 9, 4);
			add(OBSTACLE, 10, 4);
			add(OBSTACLE, 11, 4);

			add(OBSTACLE, 7, 8);
			add(OBSTACLE, 7, 9);
			add(OBSTACLE, 7, 10);
			add(OBSTACLE, 7, 11);

			add(OBSTACLE, 8, 15);
			add(OBSTACLE, 9, 15);
			add(OBSTACLE, 10, 15);
			add(OBSTACLE, 11, 15);

			add(OBSTACLE, 12, 8);
			add(OBSTACLE, 12, 9);
			add(OBSTACLE, 12, 10);
			add(OBSTACLE, 12, 11);

			// initial location of garbage
			add(GARB, 2, 0);
			add(GARB, 3, 0);
			add(GARB, 6, 0);
			add(GARB, 7, 0);
			add(GARB, 9, 1);
			add(GARB, 7, 3);
			add(GARB, 1, 5);
			add(GARB, 4, 5);
			add(GARB, 6, 5);
			add(GARB, 13, 5);
			add(GARB, 19, 5);
			add(GARB, 15, 7);
			add(GARB, 4, 8);
			add(GARB, 10, 8);
			add(GARB, 0, 9);
			add(GARB, 3, 9);
			add(GARB, 14, 9);
			add(GARB, 17, 9);
			add(GARB, 19, 9);
			add(GARB, 1, 10);
			add(GARB, 19, 13);
			add(GARB, 1, 14);
			add(GARB, 3, 15);

		}

		public boolean checkPath(int x, int y) throws Exception {
			Location r1 = getAgPos(0);
			if (r1.x < x) {
				r1.x++;
				if (model.hasObject(OBSTACLE, r1))
					return false;
			} else if (r1.x > x) {
				r1.x--;
				if (model.hasObject(OBSTACLE, r1))
					return false;
			}
			if (r1.y < y) {
				r1.y++;
				if (model.hasObject(OBSTACLE, r1))
					return false;
			} else if (r1.y > y) {
				r1.y--;
				if (model.hasObject(OBSTACLE, r1))
					return false;
			}
			return true;
		}
		void moveTowards(int x, int y) throws Exception {
			if (checkPath(x, y)) {
				Location r1 = getAgPos(0);
				if (r1.x < x)
					r1.x++;
				else if (r1.x > x)
					r1.x--;
				if (r1.y < y)
					r1.y++;
				else if (r1.y > y)
					r1.y--;
				setAgPos(0, r1);
				//setAgPos(1, getAgPos(1)); // just to draw it in the view
			} else { // find a entrepot
				Location r1 = getAgPos(0);
				Point entrepot = new Point(0, 0); // lay diem trung gian tranh chuong ngai
				double minDis = 100;  
				for (int i = 0; i < model.getWidth(); i++)
					for (int j = 0; j < model.getHeight(); j++)
						if (!model.hasObject(OBSTACLE, i, j)
								&& checkPath(i, j)
								&& ((getDistance(r1.x, r1.y, i, j) + getDistance(
										r1.x, r1.y, x, y)) < minDis)) {
							minDis = getDistance(r1.x, r1.y, i, j)
									+ getDistance(r1.x, r1.y, x, y);
							entrepot = new Point(i, j);
							System.out.println(entrepot.toString());
						}// end of if
				try {
				
			Thread.sleep(2000); // toc do xu ly hien thi nhanh or cham
					model.moveTowards(entrepot.x  , entrepot.y - 1);
				} catch (Exception e) {
					Random ra = new Random();  // chon diem ngau nhien neu ko tim duoc diem trung gian
					int randomPosition = ra.nextInt(getAgPos(0).x);
					Random ra2 = new Random();
					int randomPosition2 = ra2.nextInt(getAgPos(0).y);
					while (!model.hasObject(OBSTACLE, randomPosition , randomPosition)) {
						randomPosition = ra.nextInt((getAgPos(0).x + getAgPos(0).y)  );
						model.moveTowards(randomPosition  , randomPosition);
					}
												
				}
				// model.moveTowards(x, y); 
			}

		}

		void burnGarb() {
			// r2 location has garbage
			if (model.hasObject(GARB, getAgPos(0))) {
				remove(GARB, getAgPos(0));
			}
		}

		public double getDistance(int x1, int y1, int x2, int y2) {

			// double d= 0;
			return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
		}
	}

	class RoomView extends GridWorldView {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		// @Override
		synchronized public void initComponents(int width) {
			super.initComponents(600);
			this.getCanvas().addMouseListener(new MouseListener() {
				public void mouseClicked(MouseEvent e) {
					if (e.isShiftDown()) {
						int col = e.getX() / cellSizeW;
						int lin = e.getY() / cellSizeH;
						if (col >= 0 && lin >= 0 && col < getModel().getWidth()
								&& lin < getModel().getHeight()) {
							RoomModel mm = (RoomModel) model;
							if (!mm.hasObject(OBSTACLE, col, lin)
									&& !mm.hasObject(GARB, col, lin)) {
								mm.add(OBSTACLE, col, lin);
							}
							updatePercepts();
						}
					} else {
						int col = e.getX() / cellSizeW;
						int lin = e.getY() / cellSizeH;
						if (col >= 0 && lin >= 0 && col < getModel().getWidth()
								&& lin < getModel().getHeight()) {
							RoomModel mm = (RoomModel) model;
							if (!mm.hasObject(OBSTACLE, col, lin)
									&& !mm.hasObject(GARB, col, lin)) {
								mm.add(GARB, col, lin);
							}

							updatePercepts();
						}
					}
				}

				public void mousePressed(MouseEvent e) {
					// TODO Auto-generated method stub

				}

				public void mouseReleased(MouseEvent e) {
					// TODO Auto-generated method stub

				}

				public void mouseEntered(MouseEvent e) {
					// TODO Auto-generated method stub

				}

				public void mouseExited(MouseEvent e) {
					// TODO Auto-generated method stub

				}
			});

		}

		public RoomView(RoomModel model) {
			super(model, "Room World", 800);
			defaultFont = new Font("Arial", Font.BOLD, 10); // change default
															// font
			setVisible(true);
			repaint();

		}

		/** draw application objects */
		@Override
		public void draw(Graphics g, int x, int y, int object) {
			switch (object) {
			case room.GARB:
				drawGarb(g, x, y);
				break;
			}
		}

		@Override
		public void drawAgent(Graphics g, int x, int y, Color c, int id) {
			String label = "R" + (id + 1);
			if (id == 0) {
				c = Color.yellow;
			}
			super.drawAgent(g, x, y, c, -1); // goi ham drawAgent goc 
				g.setColor(Color.black);
			super.drawString(g, x, y, defaultFont, label);
		}

		
		public void drawGarb(Graphics g, int x, int y) {
			g.setColor(Color.green);
			super.drawObstacle(g, x, y);
			g.setColor(Color.white);
			drawString(g, x, y, defaultFont, "G");
		}

	}

}